# 计算器的界面设计
import threading
import time
from tkinter import *
import re
import max_work_hour
from tkinter.ttk import Progressbar


# 构造控件，检查输入的数字是否是正整数，且类继承Spinbox，可以很好的封装特性
class IntSpinbox(Spinbox):
    def __init__(self, master=None, cnf={}, **kw):
        s = kw.get('initvalue')
        if kw.get('initvalue') != None:
            s = kw.pop('initvalue')
        super().__init__(master, kw)
        v = '0' if s is None or len(s) == 0 else s
        self.content = StringVar(master, v)
        self.content.trace('w', self.callback)
        # self.bind('<Key>', self.on_changed)
        self.configure(textvariable=self.content)

    # 回调函数，处理输入的非数字情况
    def callback(self, *args):
        # print(args)
        s = self.content.get()
        listv = re.findall('[0-9]+', s)
        if len(listv) == 0 or 0 == int(listv[0]):
            self.content.set('0')
        else:
            v = listv[0].lstrip("0")
            cvalue = int(v)
            if cvalue > self.cget('to'):
                self.content.set(str(int(self.cget('to'))))
            else:
                self.content.set(str(v))

# 构造一个多线程的类
class ThreadWithReturnValue(threading.Thread):
    def __init__(self, *init_args, **init_kwargs):
        threading.Thread.__init__(self, *init_args, **init_kwargs)
        self._return = None

    def run(self):
        self._return = self._target(*self._args, **self._kwargs)

    def join(self):
        threading.Thread.join(self)
        return self._return


class ProcessBarDialog(Toplevel):
    def __init__(self, parent, title=None):
        Toplevel.__init__(self, parent)
        self.withdraw()
        if parent.winfo_viewable():
            self.transient(parent)
        if title:
            self.title(title)
        self.parent = parent
        self.result = None
        body = Frame(self)
        self.initial_focus = self.body(body)
        body.pack(padx=5, pady=5)

        if not self.initial_focus:
            self.initial_focus = self
        self.protocol("WM_DELETE_WINDOW", self.noCancel)

        if self.parent is not None:
            self.geometry("+%d+%d" % (parent.winfo_rootx() + parent.winfo_width() / 2,
                                      parent.winfo_rooty() + parent.winfo_height() / 2))

        self.deiconify()  # become visible now
        self.initial_focus.focus_set()
        # wait for window to appear on screen before calling grab_set
        self.wait_visibility()
        self.grab_set()
        # self.wait_window(self)
        self.pbd = Progressbar(self, maximum=1, value=0, mode="determinate")
        self.pbd.pack()


    def noCancel(self, event=None):
        pass

    def cancel(self, event=None):
        # put focus back to the parent window
        if self.parent is not None:
            self.parent.focus_set()
        self.destroy()

    def destroy(self):
        '''Destroy the window'''
        self.initial_focus = None
        Toplevel.destroy(self)

    def body(self, master):
        pass


# 显示多行文本框
class ScrollTextFrame(Text):
    def __init__(self, master=None, cnf={}, **kw):
        super().__init__(master, kw)
        self.yscrollbar = Scrollbar(master)
        self.yscrollbar.pack(side=RIGHT, fill=Y)
        self.yscrollbar.config(command=self.yview)
        self.config(yscrollcommand=self.yscrollbar.set)


# 根据Spinbox的name列表，从root中获取所有的Spinbox对象，以及相关的值
# 构造数据字典，准备计算数据
# 将计算结果在界面上输出

class WorkHourCalculator(object):
    def __init__(self):
        self.root = Tk()
        self.root.title("工时计算器")
        # 最上面显示工时上线
        self.upper_frame = Frame(self.root, name="upper_frame")
        self.lmax = Label(self.upper_frame, text="总工时上限：")
        self.tmax = IntSpinbox(self.upper_frame, from_=0, to=10000, increment=1, initvalue='120')
        self.lmax.grid(row=0, column=0, sticky="e")
        self.tmax.grid(row=0, column=1, sticky=W)
        self.upper_frame.pack()
        # 显示各系数的参数
        self.top_frame = LabelFrame(self.root, text="计算参数")
        self.l0_94 = Label(self.top_frame, text="0.94")
        self.t0_94 = IntSpinbox(self.top_frame, from_=0, to=1000, increment=1)
        self.l1_15 = Label(self.top_frame, text="1.15")
        self.t1_15 = IntSpinbox(self.top_frame, from_=0, to=1000, increment=1)
        self.l0_73 = Label(self.top_frame, text="0.73")
        self.t0_73 = IntSpinbox(self.top_frame, from_=0, to=1000, increment=1)
        self.l0_2 = Label(self.top_frame, text="0.2")
        self.t0_2 = IntSpinbox(self.top_frame, from_=0, to=1000, increment=1)
        self.l0_85 = Label(self.top_frame, text="0.85")
        self.t0_85 = IntSpinbox(self.top_frame, from_=0, to=1000, increment=1)
        self.l1_47 = Label(self.top_frame, text="1.47")
        self.t1_47 = IntSpinbox(self.top_frame, from_=0, to=1000, increment=1)
        self.l0_51 = Label(self.top_frame, text="0.51")
        self.t0_51 = IntSpinbox(self.top_frame, from_=0, to=1000, increment=1)

        self.l0_94.grid(row=0, column=0, padx=10, pady=10)
        self.t0_94.grid(row=0, column=1)
        self.l1_15.grid(row=0, column=2, padx=10, pady=10)
        self.t1_15.grid(row=0, column=3)
        self.l0_73.grid(row=1, column=0, padx=10, pady=10)
        self.t0_73.grid(row=1, column=1)
        self.l0_2.grid(row=1, column=2, padx=10, pady=10)
        self.t0_2.grid(row=1, column=3)
        self.l0_85.grid(row=2, column=0, padx=10, pady=10)
        self.t0_85.grid(row=2, column=1)
        self.l1_47.grid(row=2, column=2, padx=10, pady=10)
        self.t1_47.grid(row=2, column=3)
        self.l0_51.grid(row=3, column=0, padx=10, pady=10)
        self.t0_51.grid(row=3, column=1)
        self.top_frame.pack()

        # 按钮布局
        self.down_frame = Frame(self.root)
        self.bCal = Button(self.down_frame, {'text': '计算'}, command=self.onCalculate)
        self.bCal.grid(row=3, column=0, columnspan=4, pady=10)
        self.down_frame.pack()
        # 说明
        self.l_tip = Label(self.root, text="当系数的工时小于2时不参与计算，使用时请注意", padx=10)
        self.l_tip.pack()
        # 最后结果的显示
        self.l_cal_result = ScrollTextFrame(self.root, padx=10, height=8, state=DISABLED)
        self.l_cal_result.pack()

    def onCalculate(self, dummy=None):
        # dict_work_hour = {0.94: 46, 1.15: 66, 0.73: 22, 0.2: 20, 0.85: 22}
        self.l_cal_result.delete('1.0', END)
        dict_work_hour = {}
        dict_work_hour.update(self._ifValueMoreThanOne(0.94, self.t0_94.get()))
        dict_work_hour.update(self._ifValueMoreThanOne(1.15, self.t1_15.get()))
        dict_work_hour.update(self._ifValueMoreThanOne(0.73, self.t0_73.get()))
        dict_work_hour.update(self._ifValueMoreThanOne(0.2, self.t0_2.get()))
        dict_work_hour.update(self._ifValueMoreThanOne(0.85, self.t0_85.get()))
        dict_work_hour.update(self._ifValueMoreThanOne(1.47, self.t1_47.get()))
        dict_work_hour.update(self._ifValueMoreThanOne(0.51, self.t0_51.get()))
        max_wh = int(self.tmax.get())

        worker = max_work_hour.MaxWorkHour()
        thread_worker = ThreadWithReturnValue(target=worker.maxValueWorkHour,
                                  args=(dict_work_hour, max_wh,))
        thread_worker.start()
        toplevel = ProcessBarDialog(self.root, title="计算中")
        # pbd = Progressbar(toplevel, maximum=1, value=0, mode="determinate")
        # pbd.pack()
        while True:
            if worker.currentV == worker.maxV - 1:
                break
            else:
                toplevel.pbd["value"] = worker.currentV / (worker.maxV - 1)
            # print(pb1.cget("value"))
            toplevel.pbd.update()
            time.sleep(0.1)
        toplevel.cancel()
        max_sum, max_result = thread_worker.join()
        # max_sum, max_result = max_work_hour.maxValueWorkHour(dict_work_hour, max_wh)
        txt_show = "查询结果："
        if isinstance(max_result, list):
            for result in max_result:
                txt_show += str(result) + "\n"
        else:
            txt_show += str(max_result)
        self.l_cal_result.config(state=NORMAL)
        self.l_cal_result.insert(END, txt_show)
        self.l_cal_result.config(state=DISABLED)

    def _ifValueMoreThanOne(self, key, value):
        iV = int(value)
        return {key: iV} if iV > 1 else {}


if __name__ == "__main__":
    # root = Tk()
    # root.title("工时计算器")
    # screenwidth = root.winfo_screenwidth()
    # screenheight = root.winfo_screenheight()
    # width = 600
    # height = 600
    # alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
    # root.geometry(alignstr)
    # l0_94 = Label(root, text="0.94")
    # t0_94 = Text(root)
    # bCal = Button(root, {'text': '计算'})
    # l0_94.pack()
    # t0_94.pack()
    # bCal.pack()
    # root.mainloop()
    workhourcal = WorkHourCalculator()
    workhourcal.root.mainloop()
